<template>
    <!-- 多条件检索 -->
    <div class="public-search" @click.stop="handleOpenSearch">
        <div :class="['view', { 'inUse': isUse }]" :style="{ minWidth: minWidth }">
            <div class="search-item" v-for="(item, index) in innerActiveFilterList" :key="item.name"
                @click.stop="handleClickSearchItem(item)">
                <span class="item-label">{{ item.label }}：</span>
                <template v-if="item.inUse">
                    <component
                        :ref="`searchItem_${item.name}`"
                        :is="`search_${item.type}`"
                        :option="item"
                        :index="index"
                        @noActions="noActions"
                        @handleCloseSearch="handleCloseSearch"
                        @load="itemLoad"
                        @closeUse="closeUse"></component>
                </template>
                <template v-else>
                    <span class="format-text">{{ formatterValue(item) }}</span>
                    <i v-show="item.close" class="el-icon-circle-close close-btn item-icon-close" @click.stop="deleteSearchItem(item, index)"></i>
                </template>
            </div>
            <el-select v-model="selectValue"
                ref="searchSelect"
                :class="['search-select condition-search-select']"
                size="small"
                filterable
                placeholder="请选择筛选条件进行搜索"
                @change="handleSearchSelect"
                @focus="focusSearchSelect"
                @blur="blurSearchSelect"
                :popper-class="popperClass">
                <el-option v-for="item in notActiveFilterList" :key="item.name"
                    :value="item.name"
                    :label="item.label">
                </el-option>
            </el-select>
            <div class="btn-tools">
                <i class="el-icon-circle-close close-btn" @click.stop="clearSearchList()"></i>
                <i class="el-icon-search search-btn" @click.stop="onSearch()"></i>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: "YiPublicSearch",
    props: {
        /**
         * 开发者配置的搜索条件
         * (定义name由接口参数入参一致,保持统一语言)
         * 数据结构: [{ name: '', label: '', type: '', ... }]
         * 说明:
         * @param {Object[]} searchConfig 配置多条件检索必传参数
         * 公共参数
         * @param {String} Object[].name name为接口参数入参，唯一值
         * @param {(String|Number)} Object[].label 选项的标签
         * @param {String} Object[].type 检索组件类型 可配置[input, select, date, timeSelect, timePicker]
         * @param {Boolean} Object[].disabled 是否禁用
         * @param {String} Object[].parentName 联动性选择
         * @param {Function} Object[].changeMethod 检索组件值发生变化时触发
         * @param {Boolean} Object[].close 是否展示关闭按钮 默认 true
         * @param {(String|Number|Array|Object)} Object[].defaultValue 检索组件默认值
         * @param {(Array|String)} Object[].closeRelevanceName parentName 联动性选择 关闭联动性参数
         * select 组件参数
         * @param {Object[]} Object[].options 配置 select 组件的选项
         * @param {String} Object[].optionValueKey 配置 options 中 选项的 value 值
         * @param {String} Object[].optionLabelKey 配置 options 中 选项的 label 值
         * @param {Boolean} Object[].multiple 是否多选
         * @param {Function} Object[].removeTagMethod 多选模式下移除tag时触发
         * @param {Boolean} Object[].remote 是否为远程搜索
         * @param {Function} Object[].remoteMethod 远程搜索方法 需要配置 Object[].remote 为 true
         * date 组件参数
         * @param {String} Object[].dateType 显示类型 [year,month,date,dates,week,datetime,datetimerange,daterange,monthrange]
         * @param {String} Object[].format 显示在输入框中的格式 支持日期格式
         * @param {String} Object[].valueFormat 可选，绑定值的格式。不指定则绑定值为 Date 对象
         * @param {Object} Object[].pickerOptions 当前时间日期选择器特有的选项
         * @param {String} Object[].rangeSeparator 选择范围时的分隔符
         * @param {String} Object[].startPlaceholder 范围选择时开始日期的占位内容
         * @param {String} Object[].endPlaceholder 范围选择时结束日期的占位内容
         * @param {String[]} Object[].defaultTime 范围选择时选中日期所使用的当日内具体时刻
         * @param {Boolean} Object[].unlinkPanels 在范围选择器里取消两个日期面板之间的联动
         * time - select 组件参数
         * @param {Object} Object[].pickerOptions 当前时间日期选择器特有的选项
         * time - picker 组件参数
         * @param {Object} Object[].pickerOptions 当前时间日期选择器特有的选项
         * @param {Boolean} Object[].isRange 是否为时间范围选择
         * @param {Boolean} Object[].arrowControl 是否使用箭头进行时间选择
         * @param {String} Object[].rangeSeparator 选择范围时的分隔符
         * @param {String} Object[].startPlaceholder 范围选择时开始日期的占位内容
         * @param {String} Object[].endPlaceholder 范围选择时结束日期的占位内容
         * @param {String} Object[].valueFormat 可选，绑定值的格式。不指定则绑定值为 Date 对象
         * inputNumber 组件参数
         * @param {Number} Object[].min 设置计数器允许的最小值
         * @param {Number} Object[].max 设置计数器允许的最大值
         * @param {Number} Object[].precision 数值精度
         * cascader 组件参数
         * @param {String} Object[].separator label分隔符 默认斜杠 '/';
         * @param {Object} Object[].optionProps 组件配置选项
         * @param {Boolean} Object[].showAllLevels 输入框中是否显示选中值的完整路径 默认 true
         * @param {String} Object[].popperClass 自定义浮层类名
         * cascader - optionProps 参数 支持element-ui cascader-props 所有配置
         * @param {String} Object.expandTrigger 次级菜单的展开方式 可选值[click,hover] 默认值 click
         * @param {Boolean} Object.multiple 是否多选 默认 false
         * @param {String} Object.value 指定选项的值为选项对象的某个属性值 默认 'value'
         * @param {String} Object.label 指定选项标签为选项对象的某个属性值 默认 'label'
         * @param {String} Object.children 指定选项的子选项为选项对象的某个属性值 默认 'children'
         * @param {Boolean} Object.checkStrictly 是否严格的遵守父子节点不互相关联 默认 false
         * @param {Boolean} Object.emitPath 在选中节点改变时，是否返回由该节点所在的各级菜单的值所组成的数组，若设置 false，则只返回该节点的值 默认true
         */
        searchConfig: {
            type: Array,
            required: true
        },
        /**
         * 搜索函数
         */
        onQuery: {
            type: Function,
            required: true
        },
        minWidth: {
            type: String,
            default: "500px"
        },
        popperClass: {
            type: String
        }
    },
    /**
     * 组件配置(插件配置)
     * 可以根据不同项目配置多个根据项目需求来定义的可选组件
     */
    components: {
        search_input: () => import('./normal/input/searchInput.vue'),
        search_select: () => import('./normal/select/searchSelect.vue'),
        search_date: () => import('./normal/date/searchDate.vue'),
        search_timeSelect: () => import('./normal/time/searchTimeSelect.vue'),
        search_timePicker: () => import('./normal/time/searchTimePicker.vue'),
        search_inputNumber: () => import('./normal/inputNumber/searchInputNumber.vue'),
        search_selectTree: () => import('./normal/selectTree/searchSelectTree.vue'),
        search_cascader: () => import('./normal/cascader/searchCascader.vue')
    },
    data() {
        return {
            isUse: false, // 使用状态(true时检索框将有一个border获取焦点的交互)
            selectValue: '', // 辅助字段-防止无法打开条件下拉框
            innerActiveFilterList: [], // 已选(用户使用)选中的检索条件
            timer: null // 防止用户多次触发检索
        }
    },
    methods: {
        handleOpenSearch() { // 检索框使用-打开
            this.isUse = true;
            this.$refs.searchSelect.focus();
        },
        handleCloseSearch() { // 检索框使用-关闭
            this.isUse = false;
            this.onSearch();
        },
        /**
         * 检索框使用
         * 处理选择检索条件的change事件
         * @param {String} name 开发者配置的检索条件的 name 字段
         */
        handleSearchSelect(name) {
            if (name) {
                const activeItem = this.searchConfig.find(item => item.name === name);
                const innerActiveItem = {
                    close: true, // 不配置则默认true（不需要开发者进行配置默认值）
                    ...activeItem,
                    value: this.JSDataTypes(activeItem), // v-model 绑定的值
                    inUse: true // 用户是否在使用
                }
                this.innerActiveFilterList.push(innerActiveItem);
            }
        },
        focusSearchSelect() { // 检索框使用--focus
            this.isUse = true;
        },
        blurSearchSelect() { // 检索框使用--blur
            this.isUse = false;
        },
        clearSearchList() { // 检索框使用-清空所有搜索数据
            let clearItem = this.innerActiveFilterList.filter(item => !item.close);
            this.innerActiveFilterList = clearItem;
            this.handleCloseSearch();
        },
        /**
         * $emit 事件
         * 检索Item组件
         * 加载完成后触发
         * @param {String} name 开发者配置的检索条件的 name 字段
         */
        itemLoad(name) {
            const searchItemEl = this.$refs[`searchItem_${name}`][0];
            // searchItemEl && searchItemEl.$children[0].focus() // 源代码
            if (searchItemEl) {
                if (searchItemEl.$children[0].focus) searchItemEl.$children[0].focus();
                else searchItemEl.$children[0].toggleDropDownVisible()
            }
            this.selectValue = '';
            this.isUse = true;
        },
        /**
         * $emit 事件
         * 检索Item组件
         * 用户无任何操作行为
         * @param {Number} index 检索Item组件选择后的下标 index
         */
        noActions(index) {
            this.innerActiveFilterList.splice(index, 1);
        },
        formatterValue(item) { // 检索Item组件-用户操作完成 label 回显
            let text = "";
            if (item.type === 'select') {
                if (item.multiple) { // 多选模式
                    const selectedOptions = item.options.filter(v => item.value.includes(v[item.optionValueKey]));
                    const selectedTextList = selectedOptions.map(v => v[item.optionLabelKey]);
                    text = selectedTextList.toString();
                } else { // 单选模式
                    text = item.options.find(v => item.value === v[item.optionValueKey])[item.optionLabelKey];
                }
            } else if (item.type === 'input' || item.type === 'inputNumber') {
                text = item.value;
            } else if (item.type === 'date') {
                if (item.dateType === 'dates' || item.dateType === 'monthrange' || item.dateType === 'daterange') text = item.value.toString();
                else text = item.value;
            } else if (item.type === 'timeSelect' || item.type === 'timePicker') {
                if (item.isRange) text = item.value.toString();
                else text = item.value;
            } else if (item.type === 'selectTree') {
                if (item.selectFormatValue) { // 操作tree有值
                    text = item.selectFormatValue;
                } else { // 默认值
                    if (item.multiple) { // 多选
                        let selectFormatValue = [];
                        item.defaultValue && item.defaultValue.length > 0 && item.defaultValue.forEach(valItem => {
                            selectFormatValue.push(this.treeRecursion(item.options, valItem, item))
                        })
                        text = selectFormatValue.toString();
                    } else { // 单选
                        let selectFormatValue = this.treeRecursion(item.options, item.defaultValue, item);
                        text = selectFormatValue;
                    }
                }
            } else if (item.type === 'cascader') {
                if (item.optionProps && item.optionProps.multiple) { // 多选模式
                    let selectFormatValue = [];
                    if (!item.optionProps.emitPath) { // 仅Key展示最后一级
                        item.value.forEach(valItem => {
                            selectFormatValue.push(this.cascaderRecursion(item.options, valItem, item));
                        });
                        text = selectFormatValue.join(',');
                    } else if (item.showAllLevels) { // label多层级展示
                        item.value.forEach(valItem => {
                            let childrenFormatValue = [];
                            valItem.forEach((childrenItem) => {
                                childrenFormatValue.push(this.cascaderRecursion(item.options, childrenItem, item));
                            })
                            selectFormatValue.push(childrenFormatValue.join(item.separator || '/'));
                        });
                        text = selectFormatValue.join(',');
                    } else { // 仅展示最后一级label
                        item.value.forEach(valItem => {
                            selectFormatValue.push(this.cascaderRecursion(item.options, valItem[valItem.length - 1], item));
                        });
                        text = selectFormatValue.join(',');
                    }
                } else { // 默认 单选模式
                    if (item.optionProps && !item.optionProps.emitPath) { // 仅Key展示最后一级
                        text = this.cascaderRecursion(item.options, item.value, item);
                    } else if (item.showAllLevels) { // label多层级展示
                        let selectFormatValue = [];
                        item.value.forEach(valItem => {
                            selectFormatValue.push(this.cascaderRecursion(item.options, valItem, item));
                        });
                        text = selectFormatValue.join(item.separator || '/');
                    } else { // 仅展示最后一级label
                        text = this.cascaderRecursion(item.options, item.value[item.value.length - 1], item);
                    }
                }
            } else {
                console.error('未配置检索Item组件label回显的值');
            }
            return text;
        },
        handleClickSearchItem(item) { // 检索Item组件-重新激活Item
            item.inUse = true;
        },
        deleteSearchItem(item, index) { // 检索Item组件-删除Item
            // 需要进行关联性数据关闭
            if (item.closeRelevanceName) {
                if (Array.isArray(item.closeRelevanceName)) { // 如果是数组
                    // 删除相关联数据
                    for (let i = this.innerActiveFilterList.length - 1; i >= 0; i--) {
                        if (item.closeRelevanceName.includes(this.innerActiveFilterList[i].name)) {
                            this.innerActiveFilterList.splice(i, 1);
                        }
                    }
                    // 删除自身数据
                    this.innerActiveFilterList.splice(index, 1);
                } else {
                    for (let i = this.innerActiveFilterList.length - 1; i >= 0; i--) {
                        if (item.closeRelevanceName == this.innerActiveFilterList[i].name) {
                            this.innerActiveFilterList.splice(i, 1);
                        }
                    }
                    // 删除自身数据
                    this.innerActiveFilterList.splice(index, 1);
                }
            } else {
                this.innerActiveFilterList.splice(index, 1);
            }
            this.handleCloseSearch();
        },
        defaultValueItem() { // 检索Item组件-默认值渲染
            const defaultValueItem = this.searchConfig.filter(item => item.name && item.defaultValue);
            if (defaultValueItem && defaultValueItem.length > 0) {
                if (this.innerActiveFilterList.length === 0) {
                    defaultValueItem.forEach(item => {
                        this.innerActiveFilterList.push({
                            close: true, // 不配置则默认true（不需要开发者进行配置默认值）
                            ...item,
                            value: item.defaultValue, // v-model 绑定的值
                            inUse: false // 用户是否在使用
                        })
                    })
                    this.onSearch();
                } else {
                    const difference = this.differenceSet(defaultValueItem, this.innerActiveFilterList);
                    difference.forEach(item => {
                        this.innerActiveFilterList.push({
                            close: true, // 不配置则默认true（不需要开发者进行配置默认值）
                            ...item,
                            value: item.defaultValue, // v-model 绑定的值
                            inUse: false // 用户是否在使用
                        })
                    })
                }
            }
        },
        onSearch() { // 检索数据
            const innerActiveFilterMap = Object.create({});
            this.innerActiveFilterList.forEach(v => innerActiveFilterMap[v.name] = v.value);
            if (this.onQuery) this.onQuery(innerActiveFilterMap);
        },
        // 辅助函数
        JSDataTypes(item) {
            if (item.type === 'input') {
                return '';
            } else if (item.type === 'select') {
                return item.multiple ? [] : '';
            } else if (item.type === 'date') {
                if (item.dateType && (item.dateType === 'dates' || item.dateType === 'monthrange')) return [];
                else return '';
            } else if (item.type === 'timeSelect') {
                return '';
            } else if (item.type === 'timePicker') {
                return item.isRange ? [new Date(), new Date()] : '';
            } else if (item.type === 'inputNumber') {
                return 0;
            } else if (item.type === 'selectTree') {
                return item.multiple ? [] : '';
            } else if (item.type === 'cascader') {
                return [];
            }
        },
        // 辅助函数-差集
        differenceSet(A, B) {
            const difference = A.concat(B).filter((val) => {
                return A.find((A_item) => {
                    return A_item.name === val.name;
                }) && !B.find((B_item) => {
                    return B_item.name === val.name;
                })
            })
            return difference;
        },
        // 辅助函数 - tree 取label
        treeRecursion(tree, id, option) {
            let value = "";
            const recursion = (tree, id) => {
                tree.forEach(item => {
                    if (item[option.nodeKey] === id) {
                        value = item[option.optionTreeProps?.['label'] || 'label'];
                    } else {
                        recursion(item[option.optionTreeProps?.['children'] || 'children'] || [], id);
                    }
                })
            }
            recursion(tree, id);
            return value;
        },
        // 辅助函数 - cascader 取label
        cascaderRecursion(tree, id, option) {
            let value = "";
            const recursion = (tree, id) => {
                tree.forEach(item => {
                    if (item[option.optionProps?.['value'] || 'value'] === id) {
                        value = item[option.optionProps?.['label'] || 'label'];
                    } else {
                        recursion(item[option.optionProps?.['children'] || 'children'] || [], id);
                    }
                })
            }
            recursion(tree, id);
            return value;
        },
        // 关闭关联性使用的数据 属性 closeRelevanceName
        closeUse(closeRelevanceName) {
            if (Array.isArray(closeRelevanceName)) {
                for (let i = this.innerActiveFilterList.length - 1; i >= 0; i--) {
                    if (closeRelevanceName.includes(this.innerActiveFilterList[i].name)) {
                        this.innerActiveFilterList.splice(i, 1);
                    }
                }
            } else {
                for (let i = this.innerActiveFilterList.length - 1; i >= 0; i--) {
                    if (closeRelevanceName == this.innerActiveFilterList[i].name) {
                        this.innerActiveFilterList.splice(i, 1);
                    }
                }
            }
        }
    },
    watch: {
        searchConfig: {
            handler(val) {
                if (val && val.length > 0) this.defaultValueItem();
            },
            deep: true
        }
    },
    computed: {
        notActiveFilterList() {
            const innerActiveNames = this.innerActiveFilterList.map(item => item.name);
            const data = this.searchConfig.filter(item => {
                if (item.parentName) {
                    return !innerActiveNames.includes(item.name) && innerActiveNames.includes(item.parentName) && !item.disabled;
                } else {
                    return !innerActiveNames.includes(item.name) && !item.disabled;
                }
            });
            return data;
        }
    }
}
</script>

<style>
    @import url('./PublicSearch.css');
</style>